﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Geometries;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.GeometriesGraph
{
	public abstract class EdgeRing
	{
		protected IGeometryFactory GeometryFactory
		{
			get
			{
				return this._geometryFactory;
			}
		}

		protected EdgeRing(DirectedEdge start, IGeometryFactory geometryFactory)
		{
			this._geometryFactory = geometryFactory;
			this.ComputePoints(start);
			this.ComputeRing();
		}

		public bool IsIsolated
		{
			get
			{
				return this._label.GeometryCount == 1;
			}
		}

		public bool IsHole
		{
			get
			{
				return this._isHole;
			}
		}

		public Coordinate GetCoordinate(int i)
		{
			return this._pts[i];
		}

		public ILinearRing LinearRing
		{
			get
			{
				return this._ring;
			}
		}

		public Label Label
		{
			get
			{
				return this._label;
			}
		}

		public bool IsShell
		{
			get
			{
				return this._shell == null;
			}
		}

		public EdgeRing Shell
		{
			get
			{
				return this._shell;
			}
			set
			{
				this._shell = value;
				if (value != null)
				{
					this._shell.AddHole(this);
				}
			}
		}

		public void AddHole(EdgeRing ring)
		{
			this._holes.Add(ring);
		}

		public IPolygon ToPolygon(IGeometryFactory geometryFactory)
		{
			ILinearRing[] array = new ILinearRing[this._holes.Count];
			for (int i = 0; i < this._holes.Count; i++)
			{
				array[i] = this._holes[i].LinearRing;
			}
			return geometryFactory.CreatePolygon(this.LinearRing, array);
		}

		public void ComputeRing()
		{
			if (this._ring != null)
			{
				return;
			}
			Coordinate[] coordinates = this._pts.ToArray();
			this._ring = this._geometryFactory.CreateLinearRing(coordinates);
			this._isHole = CGAlgorithms.IsCCW(this._ring.Coordinates);
		}

		public abstract DirectedEdge GetNext(DirectedEdge de);

		public abstract void SetEdgeRing(DirectedEdge de, EdgeRing er);

		public IList<DirectedEdge> Edges
		{
			get
			{
				return this._edges;
			}
		}

		protected void ComputePoints(DirectedEdge start)
		{
			this.startDe = start;
			DirectedEdge directedEdge = start;
			bool isFirstEdge = true;
			while (directedEdge != null)
			{
				if (directedEdge.EdgeRing == this)
				{
					throw new TopologyException("Directed Edge visited twice during ring-building at " + directedEdge.Coordinate);
				}
				this._edges.Add(directedEdge);
				Label label = directedEdge.Label;
				Assert.IsTrue(label.IsArea());
				this.MergeLabel(label);
				this.AddPoints(directedEdge.Edge, directedEdge.IsForward, isFirstEdge);
				isFirstEdge = false;
				this.SetEdgeRing(directedEdge, this);
				directedEdge = this.GetNext(directedEdge);
				if (directedEdge == this.startDe)
				{
					return;
				}
			}
			throw new TopologyException("found null Directed Edge");
		}

		public int MaxNodeDegree
		{
			get
			{
				if (this._maxNodeDegree < 0)
				{
					this.ComputeMaxNodeDegree();
				}
				return this._maxNodeDegree;
			}
		}

		private void ComputeMaxNodeDegree()
		{
			this._maxNodeDegree = 0;
			DirectedEdge next = this.startDe;
			do
			{
				int outgoingDegree = ((DirectedEdgeStar)next.Node.Edges).GetOutgoingDegree(this);
				if (outgoingDegree > this._maxNodeDegree)
				{
					this._maxNodeDegree = outgoingDegree;
				}
				next = this.GetNext(next);
			}
			while (next != this.startDe);
			this._maxNodeDegree *= 2;
		}

		public void SetInResult()
		{
			DirectedEdge next = this.startDe;
			do
			{
				next.Edge.InResult = true;
				next = next.Next;
			}
			while (next != this.startDe);
		}

		protected void MergeLabel(Label deLabel)
		{
			this.MergeLabel(deLabel, 0);
			this.MergeLabel(deLabel, 1);
		}

		protected void MergeLabel(Label deLabel, int geomIndex)
		{
			Location location = deLabel.GetLocation(geomIndex, Positions.Right);
			if (location == Location.Null)
			{
				return;
			}
			if (this._label.GetLocation(geomIndex) == Location.Null)
			{
				this._label.SetLocation(geomIndex, location);
				return;
			}
		}

		protected void AddPoints(Edge edge, bool isForward, bool isFirstEdge)
		{
			Coordinate[] coordinates = edge.Coordinates;
			if (isForward)
			{
				int num = 1;
				if (isFirstEdge)
				{
					num = 0;
				}
				for (int i = num; i < coordinates.Length; i++)
				{
					this._pts.Add(coordinates[i]);
				}
				return;
			}
			int num2 = coordinates.Length - 2;
			if (isFirstEdge)
			{
				num2 = coordinates.Length - 1;
			}
			for (int j = num2; j >= 0; j--)
			{
				this._pts.Add(coordinates[j]);
			}
		}

		public bool ContainsPoint(Coordinate p)
		{
			ILinearRing linearRing = this.LinearRing;
			if (!linearRing.EnvelopeInternal.Contains(p))
			{
				return false;
			}
			if (!CGAlgorithms.IsPointInRing(p, linearRing.Coordinates))
			{
				return false;
			}
			using (List<EdgeRing>.Enumerator enumerator = this._holes.GetEnumerator())
			{
				while (enumerator.MoveNext())
				{
					if (enumerator.Current.ContainsPoint(p))
					{
						return false;
					}
				}
			}
			return true;
		}

		protected DirectedEdge startDe;

		private int _maxNodeDegree = -1;

		private readonly List<DirectedEdge> _edges = new List<DirectedEdge>();

		private readonly List<Coordinate> _pts = new List<Coordinate>();

		private readonly Label _label = new Label(Location.Null);

		private ILinearRing _ring;

		private bool _isHole;

		private EdgeRing _shell;

		private readonly List<EdgeRing> _holes = new List<EdgeRing>();

		private readonly IGeometryFactory _geometryFactory;
	}
}
